// Copyright DustedPixels.com 2008. All rights reserved.

package com.dustedpixels.asm.analysis;

import java.util.List;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.BasicInterpreter;
import org.objectweb.asm.tree.analysis.Interpreter;
import org.objectweb.asm.tree.analysis.Value;

/**
 * For each value calculates a set of possible target instructions, which are
 * using that value.
 * 
 * @author micapolos@gmail.com (Michal Pociecha-Los)
 */
public final class TargetInterpreter implements Interpreter {

  private final BasicInterpreter basicInterpreter;

  public TargetInterpreter() {
    this.basicInterpreter = new BasicInterpreter();
  }

  public Value newValue(Type type) {
    return TargetValue.like(basicInterpreter.newValue(type));
  }

  public Value newOperation(AbstractInsnNode insn) throws AnalyzerException {
    return TargetValue.like(basicInterpreter.newOperation(insn));
  }

  public Value copyOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
    ((TargetValue) value).addTarget(insn);
    return TargetValue.like(basicInterpreter.copyOperation(insn, value));
  }

  public Value unaryOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
    ((TargetValue) value).addTarget(insn);
    return TargetValue.like(basicInterpreter.unaryOperation(insn, value));
  }

  public Value binaryOperation(AbstractInsnNode insn, Value value1, Value value2)
      throws AnalyzerException {
    ((TargetValue) value1).addTarget(insn);
    ((TargetValue) value2).addTarget(insn);
    return TargetValue.like(basicInterpreter.binaryOperation(insn, value1, value2));
  }


  public Value ternaryOperation(AbstractInsnNode insn, Value value1, Value value2, Value value3)
      throws AnalyzerException {
    ((TargetValue) value1).addTarget(insn);
    ((TargetValue) value2).addTarget(insn);
    ((TargetValue) value3).addTarget(insn);
    return TargetValue.like(basicInterpreter.ternaryOperation(insn, value1, value2, value3));
  }

  @SuppressWarnings("unchecked")
  public Value naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException {
    for (Object valueObject : values) {
      ((TargetValue) valueObject).addTarget(insn);
    }
    return TargetValue.like(basicInterpreter.naryOperation(insn, values));
  }

  public Value merge(Value v, Value w) {
    if (v.equals(w)) {
      return v;
    } else {
      return TargetValue.merged((TargetValue) v, (TargetValue) w);
    }
  }
}
